/*
* Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved.
*/

#include "vpc_pipe_comm.h"
#include "securec.h"
#include "vpc_pipe_resize.h"
#include "vpc_pipe_yuvscaler.h"

static void get_yuvscaler_para(uint32_t inc, yuvscaler_para_t *para)
{
    uint32_t i;
    const struct yuv_scaler_array* coeff = search_scaler_para(inc);

    for (i = 0; i < NUM_32; i++) {
        para->taps4[i] = coeff->taps4[i];
    }

    for (i = 0; i < NUM_48; i++) {
        para->taps6[i] = coeff->taps6[i];
    }
}

static void set_yuv_scaler_para(yuvscaler_t *yuvscaler, uint32_t in_width, uint32_t in_height,
    uint32_t out_width, uint32_t out_height)
{
    const uint32_t SCALER_BIT_MOVE = 16;
    const uint32_t SCALER_RADIO_1_TIME_MILAN = 0x10000;
    if (out_width == in_width) {
        yuvscaler->ihinc = SCALER_RADIO_1_TIME_MILAN;
        yuvscaler->bypass |= 0x1;
    } else {
        yuvscaler->ihinc = ((uint64_t)(in_width) << SCALER_BIT_MOVE) / out_width;
    }

    if (out_height == in_height) {
        yuvscaler->ivinc = SCALER_RADIO_1_TIME_MILAN;
        yuvscaler->bypass |= 0x2;
    } else {
        yuvscaler->ivinc = ((uint64_t)(in_height) << SCALER_BIT_MOVE) / out_height;
    }
    if (yuvscaler->bypass == 0x3) {
        return;
    }

    yuvscaler->ihleftInt = 0;
    yuvscaler->ivtopInt = 0;
    yuvscaler->ihrightInt = yuvscaler->ihinc * (out_width - 1);
    yuvscaler->ivbotInt = yuvscaler->ivinc * (out_height - 1);

    yuvscaler->inWidth = in_width;
    yuvscaler->inHeight = in_height;
    yuvscaler->outWidth = out_width;
    yuvscaler->outHeight = out_height;
    (void)memset_s(&yuvscaler->coeff.scale, sizeof(yuvscaler->coeff.scale), 0, sizeof(yuvscaler->coeff.scale));
    get_yuvscaler_para(yuvscaler->ihinc, &yuvscaler->coeff.scale[0]);
    get_yuvscaler_para(yuvscaler->ivinc, &yuvscaler->coeff.scale[1]);
}

static void vpc_pipe_set_yuvscaler(const vpc_pipe_ctx_t *ctx, vpc_pipe_resize_para_t *para, uint32_t chn)
{
    (void)chn;
    uint32_t in_height = para->in_height;
    uint32_t in_width = para->in_width;
    uint32_t out_height = para->out_height;
    uint32_t out_width = para->out_width;
    PIPELINE_(ctx).kernel.yuvScale[MAIN_CHN].bypass = 0;

    vpc_pipe_resize_para_t pre_scale;
    pre_scale.in_width = in_width;
    pre_scale.in_height = in_height;
    pre_scale.interpolation = VPC_PIPE_YUV_SCALE;
    if (judge_use_1yuvscale(in_width, in_height, out_width, out_height) == 1) {
        set_yuv_scaler_para(&PIPELINE_(ctx).kernel.yuvScale[MAIN_CHN], in_width, in_height, out_width, out_height);
    } else if (judge_use_1prescale_1yuvscale(in_width, in_height, out_width, out_height) == 1) {
        // 模拟一个pre scale，缩小2倍
        pre_scale.out_height = in_height / NUM_2;
        pre_scale.out_width = in_width / NUM_2;
        vpc_pipe_set_resize(ctx, &pre_scale, MAIN_CHN);
        set_yuv_scaler_para(&PIPELINE_(ctx).kernel.yuvScale[MAIN_CHN],
            in_width / NUM_2, in_height / NUM_2, out_width, out_height);
    } else if (judge_use_2prescale_1yuvscale(in_width, in_height, out_width, out_height) == 1) {
        // 模拟两个pre scale，缩小4倍
        pre_scale.out_height = in_height / NUM_4;
        pre_scale.out_width = in_width / NUM_4;
        vpc_pipe_set_resize(ctx, &pre_scale, MAIN_CHN);
        set_yuv_scaler_para(&PIPELINE_(ctx).kernel.yuvScale[MAIN_CHN],
            in_width / NUM_4, in_height / NUM_4, out_width, out_height);
    } else if (judge_use_3prescale_1yuvscale(in_width, in_height, out_width, out_height) == 1) {
        // 模拟三个pre scale，缩小8倍
        pre_scale.out_height = in_height / NUM_8;
        pre_scale.out_width = in_width / NUM_8;
        vpc_pipe_set_resize(ctx, &pre_scale, MAIN_CHN);
        set_yuv_scaler_para(&PIPELINE_(ctx).kernel.yuvScale[MAIN_CHN],
            in_width / NUM_8, in_height / NUM_8, out_width, out_height);
    }
}

static void vpc_pipe_set_normalize(const vpc_pipe_ctx_t *ctx, normalize_t *para)
{
    PIPELINE_(ctx).kernel.pixaug.normalize = *para;
    PIPELINE_(ctx).kernel.pixaug.normalize.en = 1;
}

static void vpc_pipe_set_hsv(const vpc_pipe_ctx_t *ctx, hsv_t *para)
{
    PIPELINE_(ctx).kernel.hsv.deltaHue = para->deltaHue;
    PIPELINE_(ctx).kernel.hsv.scaleSat = para->scaleSat;
    PIPELINE_(ctx).kernel.hsv.hueEn = para->hueEn;
    PIPELINE_(ctx).kernel.hsv.satEn = para->satEn;
    PIPELINE_(ctx).kernel.hsv.hsv2rgbEn = 1;
    PIPELINE_(ctx).kernel.hsv.rgb2hsvEn = 1;
}

static void vpc_pipe_set_enhance(const vpc_pipe_ctx_t *ctx, enhance_t *para)
{
    PIPELINE_(ctx).kernel.pixaug.enhance = *para;
    PIPELINE_(ctx).kernel.pixaug.enhance.en = 1;
}

static void vpc_pipe_set_contrast(const vpc_pipe_ctx_t *ctx, contrast_t *para)
{
    PIPELINE_(ctx).kernel.pixaug.contrast = *para;
    PIPELINE_(ctx).kernel.pixaug.contrast.en = 1;
}

static void vpc_pipe_set_convert(const vpc_pipe_ctx_t *ctx)
{
    const uint32_t one_per_255 = 0x3b808081;
    PIPELINE_(ctx).kernel.convert.en = 1;
    PIPELINE_(ctx).kernel.convert.scaleEn = 1;
    PIPELINE_(ctx).kernel.convert.rnd = RND_NEAREST_EVEN;
    PIPELINE_(ctx).kernel.convert.scale = one_per_255;
}

static void vpc_pipe_set_clip(const vpc_pipe_ctx_t *ctx, vpc_clip_t *para)
{
    PIPELINE_(ctx).kernel.pixaug.clip = *para;
    PIPELINE_(ctx).kernel.pixaug.clip.en = 1;
}

static void vpc_pipe_set_prepadding(const vpc_pipe_ctx_t *ctx,
    vpc_pipe_make_border_info_t *para, uint32_t in_width, uint32_t in_height)
{
    PIPELINE_(ctx).kernel.prePadding.en = 1;
    PIPELINE_(ctx).kernel.prePadding.paddingSel = para->border_type;
    PIPELINE_(ctx).kernel.prePadding.mirrorMode = 0;
    if (para->border_type == VPC_PIPE_BORDER_REFLECT) { // mirror 2 ba|abc*******fgh|hg;
        PIPELINE_(ctx).kernel.prePadding.mirrorMode = NUM_1;
        PIPELINE_(ctx).kernel.prePadding.paddingSel = NUM_2;
    } else if (para->border_type == VPC_PIPE_BORDER_REFLECT_101) { // mirror 1 cb|abc****fgh|gf;
        PIPELINE_(ctx).kernel.prePadding.paddingSel = NUM_2;
    } else if (para->border_type == VPC_PIPE_BORDER_WRAP) {
        PIPELINE_(ctx).kernel.prePadding.paddingSel = NUM_3;
    }

    PIPELINE_(ctx).kernel.prePadding.bottomSize = para->bottom;
    PIPELINE_(ctx).kernel.prePadding.topSize = para->top;
    PIPELINE_(ctx).kernel.prePadding.rightSize = para->right;
    PIPELINE_(ctx).kernel.prePadding.leftSize = para->left;
    PIPELINE_(ctx).kernel.prePadding.inHeight = in_height - 1;
    PIPELINE_(ctx).kernel.prePadding.inWidth = in_width - 1;
}

static void vpc_pipe_set_postpadding(const vpc_pipe_ctx_t *ctx,
    vpc_pipe_make_border_info_t *para, uint32_t in_width, uint32_t in_height, uint32_t chn)
{
    (void)chn;
    const uint32_t MASKR = 0x00FF0000;
    const uint32_t MASKG = 0x0000FF00;
    const uint32_t MASKB = 0x000000FF;

    PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].en = 1;
    PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].paddingSel = para->border_type;
    PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].bottomSize = para->bottom;
    PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].topSize = para->top;
    PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].rightSize = para->right;
    PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].leftSize = para->left;
    PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].inHeight = in_height - 1;
    PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].inWidth = in_width - 1;

    if (para->border_type == VPC_PIPE_BORDER_CONSTANT) {
        if ((PIPELINE_(ctx).top.kernelFmt == RGB888_PACKED_FP32) || (PIPELINE_(ctx).top.kernelFmt == YUV400_FP32)) {
            PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].regValueHor[NUM_0] = para->scalar_value.val[NUM_0];
            PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].regValueHor[NUM_1] = para->scalar_value.val[NUM_1];
            PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].regValueHor[NUM_2] = para->scalar_value.val[NUM_2];
            PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].regValueVer[NUM_0] = para->scalar_value.val[NUM_0];
            PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].regValueVer[NUM_1] = para->scalar_value.val[NUM_1];
            PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].regValueVer[NUM_2] = para->scalar_value.val[NUM_2];
        } else {
            // 芯片内部地址从低到高排列时BGR, kernel格式为整型时只需使能L, 为浮点型时需要同时使能M,H
            uint32_t r_weight = para->scalar_value.val[NUM_0];
            uint32_t g_weight = para->scalar_value.val[NUM_1];
            uint32_t b_weight = para->scalar_value.val[NUM_2];
            uint32_t scalar_value = ((r_weight << NUM_16) & MASKR) |
                                    ((g_weight << NUM_8) & MASKG) |
                                    (b_weight & MASKB);
            PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].regValueHor[NUM_2] = scalar_value;
            PIPELINE_(ctx).kernel.postPadding[MAIN_CHN].regValueVer[NUM_2] = scalar_value;
        }
    }
}

static void vpc_pipe_comm_proc(const vpc_pipe_ctx_t *ctx)
{
    vpc_pipe_reset(ctx);
    vpc_pipe_set_top(ctx);
    vpc_pipe_set_in0_cvdr_channel(ctx);

    vpc_pipe_set_in0swap(ctx, ENABLE);
    vpc_pipe_set_in0uvup(ctx, ENABLE);
    vpc_pipe_set_in0_csc(ctx);
    vpc_pipe_set_in0uvdown(ctx, NULL);

    vpc_pipe_set_outuvup(ctx, MAIN_CHN, UVUP_METHOD_BUTT);
    vpc_pipe_set_out_csc(ctx, MAIN_CHN);
    vpc_pipe_set_outuvdown(ctx, MAIN_CHN, ENABLE);
    vpc_pipe_set_outswap(ctx, MAIN_CHN, ENABLE);
    vpc_pipe_set_out_cvdr_channel(ctx, MAIN_CHN);
}

static dvpp_vpc_pipe_export_func_t g_export_func = {
    // common
    .pfn_vpc_pipe_set_src_pic_info = vpc_pipe_set_src_pic_info,
    .pfn_vpc_pipe_set_src_hfbc_info = vpc_pipe_set_src_hfbc_info,
    .pfn_vpc_pipe_set_dst_pic_info = vpc_pipe_set_dst_pic_info,
    .pfn_vpc_pipe_set_apart_src_pic_info = vpc_pipe_set_apart_src_pic_info,
    .pfn_vpc_pipe_set_apart_dst_pic_info = vpc_pipe_set_apart_dst_pic_info,
    // channel
    .pfn_vpc_pipe_set_paste = vpc_pipe_set_paste,
    .pfn_vpc_pipe_in0_cvdr_copy_1chn_to_3chn = vpc_pipe_in0_cvdr_copy_1chn_to_3chn,
    .pfn_vpc_pipe_set_cvdr_rd_nr = vpc_pipe_set_cvdr_rd_nr,
    .pfn_vpc_pipe_set_output_cvdr_idle = vpc_pipe_set_output_cvdr_idle,
    .pfn_vpc_pipe_set_input_cvdr_port_for_single_chn_by_pic = vpc_pipe_set_input_cvdr_port_for_single_chn_by_pic,
    // kernel
    .pfn_vpc_pipe_set_default = vpc_pipe_set_default,
    .pfn_vpc_pipe_set_ker_fmt = vpc_pipe_set_ker_fmt,
    .pfn_vpc_pipe_set_channel_cache = vpc_pipe_set_channel_cache,
    .pfn_vpc_pipe_set_channel_out_type = vpc_pipe_set_channel_out_type,
    .pfn_vpc_pipe_set_bitwidth = vpc_pipe_set_bitwidth,
    .pfn_vpc_pipe_set_readtype = vpc_pipe_set_readtype,
    .pfn_vpc_pipe_set_outpic_size = vpc_pipe_set_outpic_size,
    .pfn_vpc_pipe_set_int_merge = vpc_pipe_set_int_merge,
    .pfn_vpc_pipe_set_errmask = vpc_pipe_set_errmask,
    .pfn_vpc_pipe_set_top_in1 = vpc_pipe_set_top_in1,
    .pfn_vpc_pipe_set_in0swap = vpc_pipe_set_in0swap,
    .pfn_vpc_pipe_set_in0uvup = vpc_pipe_set_in0uvup,
    .pfn_vpc_pipe_set_in0uvdown = vpc_pipe_set_in0uvdown,
    .pfn_vpc_pipe_set_outswap = vpc_pipe_set_outswap,
    .pfn_vpc_pipe_direct_set_outswap = vpc_pipe_direct_set_outswap,
    .pfn_vpc_pipe_set_outuvup = vpc_pipe_set_outuvup,
    .pfn_vpc_pipe_set_outuvdown = vpc_pipe_set_outuvdown,
    .pfn_vpc_pipe_set_user_csc = vpc_pipe_set_user_csc,
    .pfn_vpc_pipe_set_in0_csc = vpc_pipe_set_in0_csc,
    .pfn_vpc_pipe_set_out_csc = vpc_pipe_set_out_csc,
    .pfn_vpc_pipe_set_csc_alpha = vpc_pipe_set_csc_alpha,
    .pfn_vpc_pipe_set_crop_accelerate = vpc_pipe_set_crop_accelerate,
    .pfn_vpc_pipe_set_precrop = vpc_pipe_set_precrop,
    .pfn_vpc_pipe_set_resize = vpc_pipe_set_resize,
    .pfn_vpc_pipe_set_resize_split = vpc_pipe_set_resize_split,
    .pfn_vpc_pipe_set_postpadding = vpc_pipe_set_postpadding,
    .pfn_vpc_pipe_set_blend = vpc_pipe_set_blend,
    .pfn_vpc_pipe_set_xflip = vpc_pipe_set_xflip,
    .pfn_vpc_pipe_set_yflip = vpc_pipe_set_yflip,
    .pfn_vpc_pipe_set_histogram = vpc_pipe_set_histogram,
    .pfn_vpc_pipe_set_affine = vpc_pipe_set_affine,
    .pfn_vpc_pipe_set_filter = vpc_pipe_set_filter,
    .pfn_vpc_pipe_set_lut = vpc_pipe_set_lut,
    .pfn_vpc_pipe_set_yuvscaler = vpc_pipe_set_yuvscaler,
    .pfn_vpc_pipe_comm_proc = vpc_pipe_comm_proc,
    .pfn_vpc_pipe_subchn_comm_proc = NULL,
    // mlv1r1
    .pfn_vpc_pipe_set_normalize = vpc_pipe_set_normalize,
    .pfn_vpc_pipe_set_hsv = vpc_pipe_set_hsv,
    .pfn_vpc_pipe_set_enhance = vpc_pipe_set_enhance,
    .pfn_vpc_pipe_set_contrast = vpc_pipe_set_contrast,
    .pfn_vpc_pipe_set_convert = vpc_pipe_set_convert,
    .pfn_vpc_pipe_set_clip = vpc_pipe_set_clip,
    .pfn_vpc_pipe_set_prepadding = vpc_pipe_set_prepadding,
    // mlv1r3
    .pfn_vpc_pipe_set_mosaic = NULL,
    .pfn_vpc_pipe_set_cover = NULL,
    .pfn_vpc_pipe_set_osd = NULL,
    .pfn_vpc_pipe_set_rotate = NULL,
    .pfn_vpc_pipe_set_resize_serial = NULL,
};

void vpc_pipe_register_mlv1r1_export_func(void)
{
    dvpp_vpc_pipe_register_export_func(VPC_PIPE_CLOUD_V2, &g_export_func);
}

void vpc_pipe_unregister_mlv1r1_export_func(void)
{
    dvpp_vpc_pipe_unregister_export_func(VPC_PIPE_CLOUD_V2);
}

